import crypto from 'crypto';
import fg from 'fast-glob';
import fs from 'fs-extra';
import path from 'path';
import chardet from 'chardet';
import iconv from 'iconv-lite';

export declare type FindFileMode = '+' | '-';

export function gb2byte(gb: number): number {
    return gb * 1024 * 1024 * 1024;
}

export function getValues(o: any, keys: string[]): any[] {
    if (keys.length == 0) return [o];

    const out: any[] = [];
    const key = keys[0];
    let so = o[key];
    if (keys.length == 1) {
        if (so instanceof Array) {
            out.push(...so);
        } else {
            out.push(so);
        }
    } else {
        const leftKeys = keys.slice(1);
        if (so instanceof Array) {
            for (const sov of so) {
                out.push(...getValues(sov, leftKeys));
            }
        } else {
            out.push(...getValues(so, leftKeys));
        }
    }
    return out;
}

export function joinValues(o: any, keys: string[]): string {
    const values = [];
    for (const key of keys) {
        values.push(o[key]);
    }
    return values.join('_');
}

export function makeComments(comments: string[]): string {
    if (comments.length == 1) {
        return `/**${comments[0]}*/`;
    }
    let c = '/**\n';
    for (const s of comments) {
        c += ` * ${s}\n`;
    }
    c += '*/';
    return c;
}

export function findFiles(root: string, exts: string[], mode: FindFileMode): string[] {
    let out: string[] = [];
    if(exts) {
        for(let i = 0, len = exts.length; i < len; i++) {
            exts[i] = exts[i].toLowerCase();
        }
    }
    findFilesInternal(root, exts, mode, out);
    return out;
}

function findFilesInternal(root: string, exts: string[], mode: FindFileMode, out: string[]) {
    const files = fs.readdirSync(root);
    for(let f of files) {
        let file = path.join(root, f);
        if(fs.statSync(file).isDirectory()) {
            findFilesInternal(file, exts, mode, out);
        } else if(!exts || (mode == '+') == exts.includes(path.extname(f).toLowerCase())) {
            out.push(file);
        }
    }
}

export async function copyFiles(src: string, dst: string, pattern: string, ignore?: string[]) {
    const files = await fg(pattern, { cwd: src, ignore });
    for(let f of files) {
        await fs.copyFile(path.join(src, f), path.join(dst, f));
    }
}

export function readGB2312(file: string): string {
    let content = fs.readFileSync(file);
    return iconv.decode(content, 'gbk');
}

export async function readXml(file: string): Promise<string> {
    const content = await fs.readFile(file);
    const encoding = chardet.detect(content);
    if (encoding == 'UTF-8') {
        return await fs.readFile(file, 'utf-8');        
    }
    return iconv.decode(content, 'gbk');    
}

export function md5(str: crypto.BinaryLike): string {
    const md5 = crypto.createHash('md5');
    md5.update(str);
    str = md5.digest('hex');
    return str;  
}

export function rsubstr(str: string, from: number, length?: number): string {
    let slen = str.length;
    let r = slen - from;
    let l = -1;
    if(length) {
        l = r - length;
    } 
    if(l < 0) {
        l = 0;
    }
    return str.substring(l, r);
}

export function writeUTF8withBom(file: string, content: string) {
    fs.writeFileSync(file, '\ufeff' + content, 'utf-8');
}

export function indent(s: string, blanks = '    ', startLine = 0): string {
    let lines = s.split(/\r?\n/);
    for(let i = startLine, len = lines.length; i < len; i++) {
        let l = lines[i];
        if(l) {
            lines[i] = blanks + l;
        }
    }
    return lines.join('\n');
}

export function count(s: string, subs: string): number {
    let count = 0;
    let idx = s.indexOf(subs);
    while (idx >= 0) {
        count++;
        s = s.substring(idx + subs.length);
        idx = s.indexOf(subs);
    }
    return count;
}
